#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = 'Michael Liao'

import os, re, time, base64, hashlib, logging

import markdown2
from engine import template_engine
from feling.web import get, post, ctx, view, interceptor, seeother, notfound
from feling.web import api, Page, APIError, APIValueError, APIPermissionError, APIResourceNotFoundError
from models import User, Blog, Comment
from config import configs

_COOKIE_NAME = 'feling'
_COOKIE_KEY = configs.session['secret']

def _get_page_index():
    page_index = 1
    try:
        page_index = int(ctx.request.get('page', '1'))
    except ValueError:
        pass
    return page_index

def make_signed_cookie(id, password, max_age):
    # build cookie string by: id-expires-md5
    expires = str(int(time.time() + (max_age or 86400)))
    L = [id, expires, hashlib.md5('%s-%s-%s-%s' % (id, password, expires, _COOKIE_KEY)).hexdigest()]
    return '-'.join(L)

def parse_signed_cookie(cookie_str):
    try:
        L = cookie_str.split('-')
        if len(L) != 3:
            return None
        id, expires, md5 = L
        if int(expires) < time.time():
            return None
        user = User.get(id)
        if user is None:
            return None
        if md5 != hashlib.md5('%s-%s-%s-%s' % (id, user.password, expires, _COOKIE_KEY)).hexdigest():
            return None
        return user
    except:
        return None

def check_admin():
    user = ctx.request.user
    if user and user.admin:
        return
    raise APIPermissionError('No permission.')

@interceptor('/')
def user_interceptor(next):
    if ctx.request.path_info.startswith('/static/'):
        return next()
    logging.info('try to bind user from session cookie...')
    user = None
    cookie = ctx.request.cookies.get(_COOKIE_NAME)
    if cookie:
        logging.info('parse session cookie...')
        user = parse_signed_cookie(cookie)
        if user:
            logging.info('bind user <%s> to session...' % user.email)
            user.liveness = user.liveness + 1
            user.update()
    ctx.request.user = user
    return next()

@interceptor('/manage/')
def manage_interceptor(next):
    user = ctx.request.user
    if user and user.admin:
        return next()
    raise seeother('/sign_in')

@view('index.html')
@get('/')
def index():
    # users = User.find_all()
    # return dict(users=users, user=ctx.request.user)
    blogs = Blog.find_all()
    return dict(blogs=blogs, user=ctx.request.user)


@get('/sign_out')
def sign_out():
    ctx.response.delete_cookie(_COOKIE_NAME)
    raise seeother('/')

@view('sign_in.html')
@get('/sign_in')
def sign_in():
    ctx.request.beforesign = ctx.request.header('Referer', '/').replace(ctx.request.host,'').replace('http://','')
    #if ctx.request.user.liveness==0:
        #returndict(redirect='/', user=ctx.request.user)
    return dict(redirect=ctx.request.beforesign, user=ctx.request.user)


@api
@post('/api/sign_in')
def api_sign_in():
    i = ctx.request.input(remember='')
    email = i.email.strip().lower()
    password = i.password
    remember = i.remember
    user = User.find_first('where email=?', email)
    if user is None:
        raise APIError('auth:failed', 'email', 'Invalid email')
    elif user.password != password:
        raise APIError('auth:failed', 'password', 'Invalid password')
    # make session cookie:
    max_age = 604800 if remember=='true' else None
    cookie = make_signed_cookie(user.id, user.password, max_age)
    ctx.response.set_cookie(_COOKIE_NAME, cookie, max_age=max_age)
    # user.password = '******'
    # return user    
    return ''

_RE_EMAIL = re.compile(r'^[a-z0-9\.\-\_]+\@[a-z0-9\-\_]+(\.[a-z0-9\-\_]+){1,4}$')
_RE_MD5 = re.compile(r'^[0-9a-f]{32}$')

@api
@post('/api/sign_up')
def api_sign_up():
    i = ctx.request.input(name='', email='', password='')
    name = i.name.strip()
    email = i.email.strip().lower()
    password = i.password
    if not name:
        raise APIValueError('name')
    if not email or not _RE_EMAIL.match(email):
        raise APIValueError('email')
    if not password or not _RE_MD5.match(password):
        raise APIValueError('password')
    user = User.find_first('where name=?', name)
    if user:
        raise APIError('register:failed', 'email', 'Name is already in use.')
    user = User.find_first('where email=?', email)
    if user:
        raise APIError('register:failed', 'name', 'Email is already in use.')
    user = User(name=name, email=email, password=password, image='/static/pic/userface/default.jpeg')
    user.insert()
    # make session cookie:
    cookie = make_signed_cookie(user.id, user.password, None)
    ctx.response.set_cookie(_COOKIE_NAME, cookie)
    # user.password = '******'
    # return user
    return ''

@view('sign_up.html')
@get('/sign_up')
def register():
    ctx.request.beforesign = ctx.request.header('Referer', '/').replace(ctx.request.host,'').replace('http://','')
    return dict(redirect=ctx.request.beforesign, user=ctx.request.user)

def _get_blogs_by_page():
    total = Blog.count_all()
    page = Page(total, _get_page_index())
    blogs = Blog.find_by('order by created_at desc limit ?,?', page.offset, page.limit)
    return blogs, page




@view('/blog_create.html')
@get('/blog/create')
def create_blog():
    ctx.request.beforesign = ctx.request.header('Referer', '/').replace(ctx.request.host,'').replace('http://','')
    return dict(redirect=ctx.request.beforesign, action='/api/create_blog', user=ctx.request.user)


@api
@post('/api/create_blog')
def api_create_blog():
    i = ctx.request.input(title='', summary='', content='')
    title = i.title.strip()
    summary = i.summary.strip()
    content = i.content.strip()
    if not title:
        raise APIValueError('title', 'title cannot be empty.')
    if not summary:
        raise APIValueError('summary', 'summary cannot be empty.')
    if not content:
        raise APIValueError('content', 'content cannot be empty.')
    user = ctx.request.user
    blog = Blog(user_id=user.id, user_name=user.name, user_image = user.image, title=title, summary=summary, content=content)
    blog.insert()#让它出错会产生什么情况
    # return blog
    return ''


@view('blog_content.html')
@get('/blog/:blog_id')
def blog(blog_id):
    blog = Blog.get(blog_id)
    if blog is None:
        raise notfound()
    blog.html_content = markdown2.markdown(blog.content)
    comments, page = get_blog_comment_by_page(blog_id)
    return dict(blog=blog, comments=comments, page=page, user=ctx.request.user)


@api
@get('/api/blog')
def api_get_blogs():
    format = ctx.request.get('format', '')
    blogs, page = _get_blogs_by_page()
    if format=='html':
        for blog in blogs:
            blog.content = markdown2.markdown(blog.content)
    return dict(blogs=blogs, page=page)


@api
@get('/api/blog/:blog_id')
def api_get_blog(blog_id):
    blog = Blog.get(blog_id)
    if blog:
        return blog
    raise APIResourceNotFoundError('Blog')


@api
@post('/api/blog/:blog_id')
def api_update_blog(blog_id):
    check_admin()
    i = ctx.request.input(title='', summary='', content='')
    title = i.title.strip()
    summary = i.summary.strip()
    content = i.content.strip()
    if not title:
        raise APIValueError('title', 'title cannot be empty.')
    if not summary:
        raise APIValueError('summary', 'summary cannot be empty.')
    if not content:
        raise APIValueError('content', 'content cannot be empty.')
    blog = Blog.get(blog_id)
    if blog is None:
        raise APIResourceNotFoundError('Blog')
    blog.title = title
    blog.summary = summary
    blog.content = content
    blog.update()
    return blog

@api
@post('/api/blog/:blog_id/delete')
def api_delete_blog(blog_id):
    check_admin()
    blog = Blog.get(blog_id)
    if blog is None:
        raise APIResourceNotFoundError('Blog')
    blog.delete()
    return dict(id=blog_id)

def get_blog_comment_by_page(blog_id):
    total = Comment.count_by('where blog_id=?', blog_id)
    page = Page(total, _get_page_index(), 6)
    comments = Comment.find_by('where blog_id=? order by created_at desc limit ?,?', blog_id, page.offset, page.limit)
    return comments, page

@api
@get('/api/blog/:blog_id/comment')
def api_get_blog_comment(blog_id):
    comments, page = get_blog_comment_by_page(blog_id)
    return template_engine('comment.html', dict(blog=blog, comments=comments, page=page))


@api
@post('/api/blog/:blog_id/comment')
def api_create_blog_comment(blog_id):
    user = ctx.request.user
    if user is None:
        raise APIPermissionError('Need signin.')
    blog = Blog.get(blog_id)
    if blog is None:
        raise APIResourceNotFoundError('Blog')
    content = ctx.request.input(content='').content.strip()
    if not content:
        raise APIValueError('content')
    c = Comment(blog_id=blog_id, user_id=user.id, user_name=user.name, user_image=user.image, content=content)
    c.insert()
    # return dict(comment=c)
    comments, page = get_blog_comment_by_page(blog_id)
    return template_engine('comment.html', dict(blog=blog, comments=comments, page=page))


@api
@post('/api/comment/:comment_id/delete')
def api_delete_comment(comment_id):
    check_admin()
    comment = Comment.get(comment_id)
    if comment is None:
        raise APIResourceNotFoundError('Comment')
    comment.delete()
    return dict(id=comment_id)

@api
@get('/api/comment')
def api_get_comments():
    total = Comment.count_all()
    page = Page(total, _get_page_index())
    comments = Comment.find_by('order by created_at desc limit ?,?', page.offset, page.limit)
    return dict(comments=comments, page=page)

@api
@get('/api/user')
def api_get_users():
    total = User.count_all()
    page = Page(total, _get_page_index())
    users = User.find_by('order by created_at desc limit ?,?', page.offset, page.limit)
    for u in users:
        u.password = '******'
    return dict(users=users, page=page)